// keyof 返回索引属性名构成的联合类型
interface ACtp2 {
  name: string,
  age: number
}

let k: keyof ACtp2 // 返回索引属性名构成的联合类型
k = 'name'
k = 'age'

// k = 1  // Type '1' is not assignable to type '"name" | "age"'.

function getPropsArr<T, U extends keyof T> (obj: T, props: U[]): Array<T[U]> {
  return props.map(p => obj[p])
}

const obj1 = {
  name: 'Samuel',
  age: 18,
  gender: 'Male'
}
console.log(getPropsArr(obj1, ['gender', 'age', 'name']))

// [] 取出接口类型
interface Types {
  a: number,
  b: string,
  c: boolean,
}

type AllTypes = Types[keyof Types] // number | string | boolean

// 类型映射
type ReadOnlyTypes<T> = {
  readonly [P in keyof T]: T[P] // in 表示遍历
}

type readTypes = ReadOnlyTypes<Types>
/*
常用类型映射:
Readonly<T>  只读
Partial<T>  可选
Pick<T,K>  T里面选一个K属性
Record<K extends keyof any, T>  即将K中的每个属性([P in K]),都转为T类型,K是联合类型
 */
type readTypess = Readonly<Types>
type partialTypess = Partial<Types>

// Pick实例
function pick<T, K extends keyof T> (obj: T, props: K[]): Pick<T, K> {
  const res: any = {}
  props.map(p => {
    if (p === 'a') {
      res[p] = obj[p]
    }
  })
  return res
}

const person2: Types = {
  a: 123,
  b: 'Sam',
  c: false
}
console.log(pick(person2, ['a', 'b', 'c']))

// Record实例 将属性值映射为属性string的长度
function record<K extends string | number, T, U> (obj: Record<K, T>, fn: (x: T) => U): Record<K, U> {
  const res: any = {}
  for (const key in obj) {
    res[key] = fn(obj[key])
  }
  return res
}

console.log(record(person2, (p) => p.toString().length))

type petsGroup = 'dog' | 'cat' | 'fish';

interface IPetInfo {
  name: string,
  age: number,
}

type IPets = Record<petsGroup, IPetInfo>; // {dog: IPetInfo, cat: IPetInfo, fish: IPetInfo}

type User = {
  id: string,
  name: string,
  email: string
}

type UserWithoutEmail = Omit<User, 'email'> ;
type tExclude = Extract<User, UserWithoutEmail>;

type notCats = Exclude<petsGroup, 'cat'>;

// 类型推断
interface Proxy<T> {
  get(): T,

  set(val: T): void
}

type Proxify<T> = {
  [P in keyof T]: Proxy<T[P]> // 给T中每个属性都添加get()/set()
}

// 给一个对象的每一个属性设置set()/get()
function proxyObj<T> (obj: T): Proxify<T> {
  const res = {} as Proxify<T>
  for (const key in obj) {
    res[key] = {
      get: () => obj[key],
      set: (val) => { obj[key] = val }
    }
  }
  return res
}

const origin1 = {
  name: 'Samuel',
  age: 25
}
const proxyOrigin = proxyObj(origin1)
console.log(proxyOrigin)

// 将设置了set()/get()的obj改为原来的
function getOriginObj<T> (obj: Proxify<T>): T {
  const res = {} as T
  for (const key in obj) {
    res[key] = obj[key].get()
  }
  return res
}

const orObj = getOriginObj(proxyOrigin)
console.log(orObj)

// 显示添加/删除修饰符
type RemoveReadonly<K> = {
  -readonly [P in keyof K]: K[P] // - 表示删除修饰符,可以是readonly,?(可选修饰符）
}

type noReadOnlyTypes = RemoveReadonly<readTypess>

// unknown类型需要注意的点
// 1. 任何类型都可以赋值给unknown类型
let val1: unknown
val1 = 123
val1 = 'Sam'

// 2. 在没有类型断言或者基于控制流的类型细化时,unknown不可以赋值给其他类型
// 3. 在没有类型断言或者基于控制流的类型细化时,不可以对unknown类型对象进行任何操作
let val2: unknown
// val2++  // Object is of type 'unknown'.

// 4. unknown与其他类型组成交叉类型, 就等于其他类型
type unString = unknown & string // string

// 5. unknown与其他类型组成联合类型, 等于unknown
type anUnknown = unknown | string // unknown

// 6. never是unknown的子类型
type subUnknown = never extends unknown ? true : false // Alias for: true

// 7. keyof unknown得到never
type keyofUnknown = keyof unknown // Alias for:never

// 8. unknown类型只能进行===或!==操作, 不能进行其他操作

// 9. unknown类型不能访问属性,作为函数调用以及作为类创建对象
// 10. 使用类型映射时如果映射的属性是unknown类型,就不会产生映射
type NumsType<T> = {
  [P in keyof T]: number
}

type xxd = NumsType<any> // Initial type:{[p: string]: number}
type xxs = NumsType<unknown> // Initial type:{}

// 条件类型
type Typs<T> = T extends string ? string : number
let vals2: Typs<'2'> // let vals2: string
let vals3: Typs<false> // let vals3: number  走后面的逻辑

// 分布式条件类型
type onlyString<T> = (T extends string ? T : never)
type someTypes = 'a' | 'b' | 123 | false
type resultTypes = onlyString<someTypes> // Initial type: "a" | "b"

// 条件类型加索引类型
type reserveFunction<T> = {
  [P in keyof T]: T[P] extends () => void ? P : never // 取出所有是函数类型的属性P, 其他为never
}[keyof T] // 索引属性来去掉never属性

interface Part {
  user: 'Sam',
  code: 132,
  subparts: Part[]
  updateTime: () => void,

  thankU(): void
}

type Res2 = reserveFunction<Part>
const obj2: Res2 = 'updateTime'

// 推断元素类型
type Type2<T> = T extends any[] ? T[number] : T // 如果T是一个数组, 那就取T元素的类型(当元素类型不一样时是联合类型)
type typesX = Type2<string[]> // Initial type:string
type typesX2 = Type2<boolean[]> // Initial type:boolean

// // infer 推断元素类型
type Type3<T> = T extends Array<infer U> ? U : T
type typesY = Type3<string[]> // Initial type:string
type typesY2 = Type3<boolean[]> // Initial type:boolean
type typesY3 = Type3<[false, 123, 'sam']> // Initial type:false | 123 | "sam"

// 内置条件类型
// Exclude<T,U>  从T里面排除U
type typesA = Exclude<'a' | 'b' | 'c', 'a' | 'b'> // Initial type:"c"

// Extract<T,U>  从T中选取可以赋值给U的类型
type typesB = Extract<'a' | 'b' | 'c', 'a' | 'b'> // Initial type:"a" | "b"

// NonNullable<T>
type typesC = NonNullable<'a' | 'b' | 'c' | null | undefined> // Initial type:"a" | "b" | "c"

// ReturnType<T>  T的返回值类型
type typesD = ReturnType<() => string> // Initial type:string

// InstanceType<T>   ts中类有2种类型, 静态部分的类型和实例的类型, 所以T如果是构造函数类型, 那么InstanceType可以返回他的实例类型:
class AClass {
  constructor (public name: string) {
    console.log('AClass')
  }
}

interface AConstructor {
  new(): string
}

type typeE = InstanceType<typeof AClass> // AClass
type typeF = InstanceType<AConstructor> // string
type typeG = ConstructorParameters<typeof AClass>

interface AConstructor {
  new(a: number): string[];
}

type ConstructorParameterss<T extends new(...arg: any) => any> = T extends new(...arg: infer P) => any ? P : never

type A1 = ConstructorParameterss<AConstructor> // [number]
